Skip to content

Conversation

@ZocoLini
Copy link
Collaborator

@ZocoLini ZocoLini commented Dec 30, 2025

The peers and peers reputation storage logic moved into the storage module following the trait pattern introduced by PR #311. Also moved PeerReputation values validation logic into serde's pipeline

This PR is built on top of #311

Summary by CodeRabbit

  • New Features

    • Introduces a unified persistent peer storage for on-disk peers and reputations, with improved validation and bounded reputation fields.
  • Bug Fixes

    • More robust save/load using atomic writes and improved corruption handling and clamping of invalid reputation values.
  • Tests

    • Tests updated to exercise the new storage-backed persistence.
  • Chores

    • Removed legacy peer persistence surface and adjusted repository ignore rules for reputation files.

✏️ Tip: You can customize this high-level summary in your review settings.

@ZocoLini ZocoLini marked this pull request as draft December 30, 2025 16:59
@github-actions github-actions bot added the merge-conflict The PR conflicts with the target branch. label Dec 30, 2025
@github-actions
Copy link

This PR has merge conflicts with the base branch. Please rebase or merge the base branch into your branch to resolve them.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

📝 Walkthrough

Walkthrough

Peer persistence was migrated from a file-backed PeerStore to a storage abstraction (PeerStorage / PersistentPeerStorage). The old persist module was removed; network, reputation, storage, and tests were updated to use the new storage API and on-disk layout under a peers folder.

Changes

Cohort / File(s) Summary
New Peer Storage
dash-spv/src/storage/peers.rs
Added PeerStorage trait and PersistentPeerStorage implementing persistent peers and reputations; atomically reads/writes peers.dat and reputations.json, plus unit tests.
Storage Exports
dash-spv/src/storage/mod.rs
Added mod peers; and public re-exports: pub use crate::storage::peers::{PeerStorage, PersistentPeerStorage};.
Remove Legacy Persist
dash-spv/src/network/persist.rs
File deleted: removed public PeerStore, its constructor and methods (save_peers, load_peers, clear), serialization structs, and tests.
Network Manager Migration
dash-spv/src/network/manager.rs
peer_store field changed from Arc<PeerStore> to Arc<PersistentPeerStorage>; initialization now uses PersistentPeerStorage::open(...); all save/load calls use the storage handle.
Reputation Persistence Changes
dash-spv/src/network/reputation.rs
Switched to storage-backed persistence (save_to_storage/load_from_storage accept PeerStorage); PeerReputation now derives Serialize/Deserialize with per-field clamps and new constants for bounds; removed in-file serialized types.
Network Module Cleanup
dash-spv/src/network/mod.rs
Removed pub mod persist export.
Test Updates
dash-spv/src/network/reputation_tests.rs, dash-spv/tests/wallet_integration_test.rs
Tests updated to use PersistentPeerStorage handles and temporary storage paths (ClientConfig::with_storage_path) instead of direct file paths.
Gitignore Change
.gitignore
Removed entries for /dash-spv-ffi/peer_reputation.json and /dash-spv/peer_reputation.json.

Sequence Diagram(s)

sequenceDiagram
  participant Manager as PeerNetworkManager
  participant Storage as PersistentPeerStorage
  participant FS as Filesystem

  Note over Manager,Storage: Saving reputations
  Manager->>Storage: save_peers_reputation(reputations)
  Storage->>FS: atomic_write(reputations.json)
  FS-->>Storage: write_ok
  Storage-->>Manager: ok

  Note over Manager,Storage: Loading peers on startup
  Manager->>Storage: load_peers()
  Storage->>FS: read(peers.dat)
  FS-->>Storage: data
  Storage-->>Manager: Vec<SocketAddr>
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰
I hopped through folders, found a change,
Old files gone, new paths in range,
Peers tucked safe in peers/ they stay,
Reputations saved the storage way,
A merry thump — persistence, hooray! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Refactor/peer persistence' directly describes the main objective of the PR, which reorganizes peer and reputation persistence logic into the storage module using trait patterns.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ZocoLini ZocoLini changed the base branch from v0.41-dev to refactor/storage-manager-trait December 30, 2025 16:59
@ZocoLini ZocoLini force-pushed the refactor/peer-persistence branch from 5d78f43 to 2d4550e Compare December 30, 2025 17:01
@github-actions github-actions bot removed the merge-conflict The PR conflicts with the target branch. label Dec 30, 2025
@ZocoLini ZocoLini force-pushed the refactor/storage-manager-trait branch from 2bc0490 to e40b953 Compare January 5, 2026 16:28
@github-actions github-actions bot added the merge-conflict The PR conflicts with the target branch. label Jan 5, 2026
@github-actions
Copy link

github-actions bot commented Jan 5, 2026

This PR has merge conflicts with the base branch. Please rebase or merge the base branch into your branch to resolve them.

@ZocoLini ZocoLini force-pushed the refactor/storage-manager-trait branch from e40b953 to 0248257 Compare January 5, 2026 16:44
Base automatically changed from refactor/storage-manager-trait to v0.42-dev January 8, 2026 01:54
@ZocoLini ZocoLini force-pushed the refactor/peer-persistence branch from 3ca989d to 4a7925a Compare January 8, 2026 21:54
@github-actions github-actions bot removed the merge-conflict The PR conflicts with the target branch. label Jan 8, 2026
@ZocoLini ZocoLini marked this pull request as ready for review January 8, 2026 21:55
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
dash-spv/src/storage/mod.rs (1)

209-250: Missing peers re-initialization in clear() method.

After clearing storage and re-opening all storage components (lines 233-244), the peers storage is not re-initialized. This will leave self.peers pointing to the old storage instance with stale data, potentially causing issues after a clear() operation.

🐛 Add peers re-initialization in clear()
         self.masternodestate =
             Arc::new(RwLock::new(PersistentMasternodeStateStorage::open(storage_path).await?));
+        self.peers = Arc::new(RwLock::new(PersistentPeerStorage::open(storage_path).await?));
 
         // Restart the background worker for future operations
         self.start_worker().await;
🤖 Fix all issues with AI agents
In @dash-spv/src/storage/peers.rs:
- Around line 79-94: Remove the blocking std::fs::create_dir_all call and its
unwrap; instead compute the parent once using peers_file.parent().ok_or(...) (as
in peers_file_parent) and call the async
tokio::fs::create_dir_all(peers_file_parent).await to create directories;
eliminate the duplicate directory creation block and ensure any errors are
mapped to StorageError::WriteFailed where appropriate (keep the existing buffer
encoding loop and error handling for item.consensus_encode).
- Around line 101-127: In load_peers, replace blocking std::fs operations with
async equivalents: use tokio::fs::try_exists(peers_file) to check existence,
open the file with tokio::fs::File::open and wrap it in tokio::io::BufReader, or
simpler use tokio::fs::read(peers_file) to load the whole file into a Vec<u8>
and decode from a std::io::Cursor; then feed that reader/bytes to
AddrV2Message::consensus_decode (or its slice-based variant) instead of using
the blocking BufReader/File and std::fs::exists; ensure imports include
tokio::fs::File, tokio::fs::try_exists, tokio::fs::read and tokio::io::BufReader
and update error handling around consensus_decode accordingly.
- Around line 148-159: In load_peers_reputation, replace the blocking
reputation_file.exists() call with the async
tokio::fs::try_exists(reputation_file).await and handle its Result (e.g., let
exists = tokio::fs::try_exists(reputation_file).await?; if !exists { return
Ok(HashMap::new()); }) so the function stays fully async and errors propagate
consistently alongside the existing tokio::fs::read_to_string(...).await?.
🧹 Nitpick comments (3)
dash-spv/tests/wallet_integration_test.rs (1)

18-31: Inconsistent storage paths between network and disk storage managers.

The test creates two separate temporary directories:

  1. Line 18: temp_dir used for ClientConfig.with_storage_path() → used by PeerNetworkManager for peer storage
  2. Line 29: A new TempDir for DiskStorageManager

This means peer data and other storage (headers, filters, etc.) are written to different directories. While this works for basic testing, it creates an inconsistency that could mask integration issues where peers and storage should share the same root path.

Consider using a single temporary directory for both:

♻️ Suggested fix to use consistent storage paths
 async fn create_test_client(
 ) -> DashSpvClient<WalletManager<ManagedWalletInfo>, PeerNetworkManager, DiskStorageManager> {
     let temp_dir = tempfile::TempDir::new().expect("Failed to create temporary directory");
+    let storage_path = temp_dir.path().to_path_buf();
+    
     let config = ClientConfig::testnet()
         .without_filters()
         .without_masternodes()
-        .with_storage_path(temp_dir.path().to_path_buf());
+        .with_storage_path(storage_path.clone());
 
     // Create network manager
     let network_manager = PeerNetworkManager::new(&config).await.unwrap();
 
     // Create storage manager
-    let storage_manager =
-        DiskStorageManager::new(TempDir::new().expect("Failed to create tmp dir").path())
-            .await
-            .expect("Failed to create tmp storage");
+    let storage_manager = DiskStorageManager::new(&storage_path)
+        .await
+        .expect("Failed to create tmp storage");
+
+    // Keep temp_dir alive by leaking it (or store it in the client if possible)
+    std::mem::forget(temp_dir);

Note: If you keep separate directories intentionally for test isolation, consider adding a comment explaining this design choice.

dash-spv/tests/header_sync_test.rs (1)

264-280: Same storage path inconsistency as wallet_integration_test.rs.

Similar to the wallet integration test, this creates two separate temporary directories - one for ClientConfig (used by PeerNetworkManager) and another for DiskStorageManager. This means peer storage and block header storage will be in different locations.

For consistency with production behavior where all storage shares a common root, consider using the same path for both managers.

dash-spv/src/storage/peers.rs (1)

162-206: Consider adding tests for reputation persistence methods.

The tests cover peer save/load well, but save_peers_reputation and load_peers_reputation are not tested. Adding tests for these methods would improve coverage and confidence in the serialization/deserialization logic.

💡 Example test structure
#[tokio::test]
async fn test_persistent_peer_storage_reputation_save_load() {
    let temp_dir = TempDir::new().expect("Failed to create temporary directory");
    let store = PersistentPeerStorage::open(temp_dir.path())
        .await
        .expect("Failed to open persistent peer storage");

    let mut reputations = HashMap::new();
    let addr: SocketAddr = "192.168.1.1:9999".parse().unwrap();
    let reputation = PeerReputation::default(); // or construct with specific values
    reputations.insert(addr, reputation);

    store.save_peers_reputation(&reputations)
        .await
        .expect("Failed to save reputations");

    let loaded = store.load_peers_reputation()
        .await
        .expect("Failed to load reputations");
    
    assert_eq!(loaded.len(), 1);
    assert!(loaded.contains_key(&addr));
}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 189a66d and 4a7925a.

📒 Files selected for processing (11)
  • .gitignore
  • dash-spv/src/network/manager.rs
  • dash-spv/src/network/mod.rs
  • dash-spv/src/network/persist.rs
  • dash-spv/src/network/reputation.rs
  • dash-spv/src/network/reputation_tests.rs
  • dash-spv/src/storage/mod.rs
  • dash-spv/src/storage/peers.rs
  • dash-spv/tests/handshake_test.rs
  • dash-spv/tests/header_sync_test.rs
  • dash-spv/tests/wallet_integration_test.rs
💤 Files with no reviewable changes (3)
  • dash-spv/src/network/persist.rs
  • dash-spv/src/network/mod.rs
  • .gitignore
🧰 Additional context used
📓 Path-based instructions (8)
dash-spv/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

dash-spv/**/*.rs: Use async/await throughout the codebase, built on tokio runtime
Use Arc for trait objects to enable runtime polymorphism for NetworkManager and StorageManager
Use Tokio channels for inter-component message passing between async tasks
Maintain minimum Rust version (MSRV) of 1.89 and use only compatible syntax and features

Files:

  • dash-spv/tests/wallet_integration_test.rs
  • dash-spv/tests/handshake_test.rs
  • dash-spv/tests/header_sync_test.rs
  • dash-spv/src/storage/mod.rs
  • dash-spv/src/network/reputation_tests.rs
  • dash-spv/src/network/manager.rs
  • dash-spv/src/network/reputation.rs
  • dash-spv/src/storage/peers.rs
dash-spv/tests/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

dash-spv/tests/**/*.rs: Organize tests into unit tests (in-module), integration tests (tests/ directory), real network tests (with live Dash Core nodes), and performance benchmarks
Integration tests should gracefully handle unavailable Dash Core nodes at 127.0.0.1:9999 without failing the test suite

Files:

  • dash-spv/tests/wallet_integration_test.rs
  • dash-spv/tests/handshake_test.rs
  • dash-spv/tests/header_sync_test.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: MSRV (Minimum Supported Rust Version) is 1.89; ensure compatibility with this version for all builds
Unit tests should live alongside code with #[cfg(test)] annotation; integration tests use the tests/ directory
Use snake_case for function and variable names
Use UpperCamelCase for types and traits
Use SCREAMING_SNAKE_CASE for constants
Format code with rustfmt before commits; ensure cargo fmt --all is run
Run cargo clippy --workspace --all-targets -- -D warnings for linting; avoid warnings in CI
Prefer async/await via tokio for asynchronous operations

**/*.rs: Never hardcode network parameters, addresses, or keys in Rust code
Use proper error types (thiserror) and propagate errors appropriately in Rust
Use tokio runtime for async operations in Rust
Use conditional compilation with feature flags for optional features
Write unit tests for new functionality in Rust
Format code using cargo fmt
Run clippy with all features and all targets, treating warnings as errors
Never log or expose private keys in any code
Always validate inputs from untrusted sources in Rust
Use secure random number generation for keys

Files:

  • dash-spv/tests/wallet_integration_test.rs
  • dash-spv/tests/handshake_test.rs
  • dash-spv/tests/header_sync_test.rs
  • dash-spv/src/storage/mod.rs
  • dash-spv/src/network/reputation_tests.rs
  • dash-spv/src/network/manager.rs
  • dash-spv/src/network/reputation.rs
  • dash-spv/src/storage/peers.rs
**/tests/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/tests/**/*.rs: Use descriptive test names (e.g., test_parse_address_mainnet)
Mark network-dependent or long-running tests with #[ignore] and run with -- --ignored

Files:

  • dash-spv/tests/wallet_integration_test.rs
  • dash-spv/tests/handshake_test.rs
  • dash-spv/tests/header_sync_test.rs
**/*integration*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

Write integration tests for network operations

Files:

  • dash-spv/tests/wallet_integration_test.rs
**/*test*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*test*.rs: Test both mainnet and testnet configurations
Use proptest for property-based testing where appropriate

Files:

  • dash-spv/tests/wallet_integration_test.rs
  • dash-spv/tests/handshake_test.rs
  • dash-spv/tests/header_sync_test.rs
  • dash-spv/src/network/reputation_tests.rs
dash-spv/src/storage/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

Store headers in 10,000-header segments with index files in headers/ directory, filter headers and compact block filters in filters/ directory, and state in state/ directory

Files:

  • dash-spv/src/storage/mod.rs
  • dash-spv/src/storage/peers.rs
dash-spv/src/network/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

dash-spv/src/network/**/*.rs: Use DNS-first peer discovery with automatic DNS seeds (dnsseed.dash.org, testnet-seed.dashdot.io) when no explicit peers are configured; implement immediate startup with 10-second delay only for subsequent peer searches
Implement configurable timeouts with recovery mechanisms for network operations

Files:

  • dash-spv/src/network/reputation_tests.rs
  • dash-spv/src/network/manager.rs
  • dash-spv/src/network/reputation.rs
🧠 Learnings (27)
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/tests/**/*.rs : Organize tests into unit tests (in-module), integration tests (tests/ directory), real network tests (with live Dash Core nodes), and performance benchmarks

Applied to files:

  • dash-spv/tests/wallet_integration_test.rs
  • dash-spv/tests/handshake_test.rs
  • dash-spv/tests/header_sync_test.rs
  • dash-spv/src/network/reputation_tests.rs
  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/client/**/*.rs : Use the DashSpvClient high-level API with proper configuration via ClientConfig for client initialization

Applied to files:

  • dash-spv/tests/wallet_integration_test.rs
  • dash-spv/tests/handshake_test.rs
  • dash-spv/tests/header_sync_test.rs
  • dash-spv/src/storage/mod.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/tests/**/*.rs : Organize unit tests by functionality: separate test files for BIP32, mnemonics, addresses, derivation paths, and PSBT operations

Applied to files:

  • dash-spv/tests/wallet_integration_test.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/tests/**/*.rs : Integration tests should gracefully handle unavailable Dash Core nodes at 127.0.0.1:9999 without failing the test suite

Applied to files:

  • dash-spv/tests/wallet_integration_test.rs
  • dash-spv/tests/handshake_test.rs
  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-30T22:00:57.000Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-30T22:00:57.000Z
Learning: Applies to **/*test*.rs : Test both mainnet and testnet configurations

Applied to files:

  • dash-spv/tests/wallet_integration_test.rs
  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-06-26T16:01:37.609Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:01:37.609Z
Learning: The mempool tracking infrastructure (UnconfirmedTransaction, MempoolState, configuration, and mempool_filter.rs) is fully implemented and integrated in the Dash SPV client as of this PR, including client logic, FFI APIs, and tests.

Applied to files:

  • dash-spv/tests/wallet_integration_test.rs
  • dash-spv/src/storage/mod.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/tests/unit/**/*.rs : Add corresponding unit tests in `tests/unit/` for each new FFI function

Applied to files:

  • dash-spv/tests/wallet_integration_test.rs
  • dash-spv/src/network/reputation_tests.rs
  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/storage/**/*.rs : Store headers in 10,000-header segments with index files in headers/ directory, filter headers and compact block filters in filters/ directory, and state in state/ directory

Applied to files:

  • dash-spv/tests/wallet_integration_test.rs
  • dash-spv/tests/handshake_test.rs
  • dash-spv/tests/header_sync_test.rs
  • dash-spv/src/storage/mod.rs
  • dash-spv/src/network/manager.rs
  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/tests/**/*.rs : Use deterministic testing with known test vectors and fixed seeds for reproducible results

Applied to files:

  • dash-spv/tests/wallet_integration_test.rs
  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-22T17:59:51.097Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-22T17:59:51.097Z
Learning: Cover critical parsing, networking, SPV, and wallet flows in tests; add regression tests for fixes; consider property tests with `proptest`

Applied to files:

  • dash-spv/tests/wallet_integration_test.rs
  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/network/**/*.rs : Use DNS-first peer discovery with automatic DNS seeds (dnsseed.dash.org, testnet-seed.dashdot.io) when no explicit peers are configured; implement immediate startup with 10-second delay only for subsequent peer searches

Applied to files:

  • dash-spv/tests/handshake_test.rs
  • dash-spv/src/storage/mod.rs
  • dash-spv/src/network/reputation_tests.rs
  • dash-spv/src/network/manager.rs
  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/**/*.rs : Use Arc<dyn TraitName> for trait objects to enable runtime polymorphism for NetworkManager and StorageManager

Applied to files:

  • dash-spv/tests/handshake_test.rs
  • dash-spv/src/storage/mod.rs
  • dash-spv/src/network/manager.rs
  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/network/**/*.rs : Implement configurable timeouts with recovery mechanisms for network operations

Applied to files:

  • dash-spv/tests/handshake_test.rs
  • dash-spv/src/network/manager.rs
📚 Learning: 2025-06-26T15:54:02.509Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:54:02.509Z
Learning: The `StorageManager` trait in `dash-spv/src/storage/mod.rs` uses `&mut self` methods but is also `Send + Sync`, and implementations often use interior mutability for concurrency. This can be confusing, so explicit documentation should clarify thread-safety expectations and the rationale for the API design.

Applied to files:

  • dash-spv/tests/handshake_test.rs
  • dash-spv/tests/header_sync_test.rs
  • dash-spv/src/storage/mod.rs
  • dash-spv/src/network/manager.rs
  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Use thread-local storage for error propagation via `dash_spv_ffi_get_last_error()` function

Applied to files:

  • dash-spv/tests/handshake_test.rs
  • dash-spv/src/storage/mod.rs
  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/error.rs : Use domain-specific error types (NetworkError, StorageError, SyncError, ValidationError, SpvError) rather than generic error handling

Applied to files:

  • dash-spv/tests/handshake_test.rs
  • dash-spv/src/storage/mod.rs
  • dash-spv/src/network/manager.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/sync/**/*.rs : Implement sequential phase-based synchronization via SyncManager with phases progressing in order: Headers → Masternode List → Filter Headers → Filters → Blocks

Applied to files:

  • dash-spv/tests/header_sync_test.rs
  • dash-spv/src/storage/mod.rs
  • dash-spv/src/network/manager.rs
  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-30T22:00:57.000Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-30T22:00:57.000Z
Learning: Applies to **/*integration*.rs : Write integration tests for network operations

Applied to files:

  • dash-spv/tests/header_sync_test.rs
  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/**/*.rs : Use async/await throughout the codebase, built on tokio runtime

Applied to files:

  • dash-spv/src/storage/mod.rs
  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/**/*.rs : Use Tokio channels for inter-component message passing between async tasks

Applied to files:

  • dash-spv/src/storage/mod.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Follow a layered, trait-based architecture with clear separation of concerns across modules: client/, network/, storage/, sync/, validation/, wallet/, with trait-based abstractions for swappable implementations

Applied to files:

  • dash-spv/src/storage/mod.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/*.rs : Use `BTreeMap` for ordered data (accounts, transactions) and `HashMap` for lookups (address mappings); apply memory management strategies for old transaction data

Applied to files:

  • dash-spv/src/storage/mod.rs
📚 Learning: 2025-06-26T16:02:42.390Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:02:42.390Z
Learning: Passing exclusive mutable references to network and storage managers into the sync manager in async Rust can lead to borrow checker issues or runtime contention if concurrent access is needed elsewhere. It's advisable to document this architectural tradeoff and consider refactoring to interior mutability or message passing for shared access as the codebase evolves.

Applied to files:

  • dash-spv/src/storage/mod.rs
  • dash-spv/src/network/manager.rs
📚 Learning: 2025-12-22T17:59:51.097Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-22T17:59:51.097Z
Learning: Applies to **/*.rs : Unit tests should live alongside code with `#[cfg(test)]` annotation; integration tests use the `tests/` directory

Applied to files:

  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-22T17:59:51.097Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-22T17:59:51.097Z
Learning: Applies to **/tests/**/*.rs : Use descriptive test names (e.g., `test_parse_address_mainnet`)

Applied to files:

  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-22T17:59:51.097Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-22T17:59:51.097Z
Learning: Applies to **/tests/**/*.rs : Mark network-dependent or long-running tests with `#[ignore]` and run with `-- --ignored`

Applied to files:

  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-30T22:00:57.000Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-30T22:00:57.000Z
Learning: Applies to **/*.rs : Write unit tests for new functionality in Rust

Applied to files:

  • dash-spv/src/network/reputation_tests.rs
🧬 Code graph analysis (6)
dash-spv/tests/handshake_test.rs (2)
dash-spv/src/network/manager.rs (1)
  • new (78-117)
dash-spv/src/client/config.rs (1)
  • new (210-217)
dash-spv/tests/header_sync_test.rs (2)
dash-spv/src/network/manager.rs (1)
  • new (78-117)
dash-spv/src/storage/mod.rs (1)
  • new (99-143)
dash-spv/src/storage/mod.rs (2)
dash-spv/src/network/manager.rs (2)
  • new (78-117)
  • clone (1036-1059)
dash-spv/src/storage/peers.rs (1)
  • open (57-63)
dash-spv/src/network/reputation_tests.rs (3)
dash-spv/src/network/manager.rs (1)
  • new (78-117)
dash-spv/src/storage/mod.rs (2)
  • new (99-143)
  • open (52-52)
dash-spv/src/storage/peers.rs (1)
  • open (57-63)
dash-spv/src/network/manager.rs (2)
dash-spv/src/storage/peers.rs (1)
  • open (57-63)
dash-spv/src/storage/mod.rs (2)
  • open (52-52)
  • new (99-143)
dash-spv/src/storage/peers.rs (3)
dash-spv/src/storage/io.rs (1)
  • atomic_write (24-57)
dash-spv/src/storage/mod.rs (4)
  • open (52-52)
  • persist (54-54)
  • persist (193-204)
  • new (99-143)
dash-spv/src/network/manager.rs (1)
  • new (78-117)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
  • GitHub Check: Windows / tools
  • GitHub Check: Ubuntu / ffi
  • GitHub Check: Ubuntu ARM / tools
  • GitHub Check: macOS / wallet
  • GitHub Check: macOS / ffi
  • GitHub Check: fuzz (hashes_sha512_256)
  • GitHub Check: MSRV Check (1.89)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (hashes_sha256)
  • GitHub Check: fuzz (dash_outpoint_string)
  • GitHub Check: fuzz (dash_deserialize_block)
  • GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
  • GitHub Check: Pre-commit (macos-latest)
  • GitHub Check: Pre-commit (windows-latest)
  • GitHub Check: Pre-commit (ubuntu-latest)
  • GitHub Check: Thread Sanitizer
  • GitHub Check: Address Sanitizer
  • GitHub Check: conflict-check
🔇 Additional comments (17)
dash-spv/tests/handshake_test.rs (1)

76-77: LGTM! Proper test isolation with temporary storage.

The test now correctly uses a temporary directory for storage, ensuring test isolation and alignment with the new PersistentPeerStorage backend. The temp_dir lifetime is appropriate as it persists for the test duration.

dash-spv/src/network/reputation_tests.rs (2)

5-6: LGTM! Import aligns with new storage architecture.

The import correctly brings in PersistentStorage from the storage module, supporting the storage-backed persistence pattern.


66-73: LGTM! Test correctly migrated to storage-backed persistence.

The test properly:

  1. Creates a temporary directory for isolation
  2. Opens PersistentPeerStorage with the temp path
  3. Uses storage handles for save_to_storage and load_from_storage

This aligns with the PR's goal of moving to storage-backed persistence.

dash-spv/src/storage/mod.rs (3)

8-12: LGTM! Module declarations for new storage components.

The new io and peers modules are correctly declared in the module tree.


43-43: LGTM! Public exports for peer storage types.

The PeerStorage trait and PersistentPeerStorage implementation are correctly exported, making them available to the network module.


90-90: LGTM! Peers storage properly integrated into DiskStorageManager.

The peers field is:

  • Correctly typed as Arc<RwLock<PersistentPeerStorage>>
  • Properly initialized in new()
  • Cloned for the background worker
  • Persisted in both the periodic worker loop and explicit persist() calls

This follows the same pattern as other storage components.

Also applies to: 133-133, 162-162, 179-179, 203-203

dash-spv/src/network/manager.rs (3)

30-30: LGTM! Updated imports for storage-backed persistence.

The imports correctly bring in PeerStorage trait, PersistentPeerStorage implementation, and PersistentStorage trait for the new storage architecture.


42-42: LGTM! PeerNetworkManager migrated to PersistentPeerStorage.

The peer store field and initialization are correctly updated:

  • Field type changed from PeerStore to PersistentPeerStorage
  • Uses PersistentStorage::open() trait method for initialization
  • Reputation data is loaded on startup with graceful error handling

Also applies to: 84-90


741-743: LGTM! Reputation persistence uses new storage interface.

The save_to_storage calls in both the maintenance loop and shutdown path correctly pass the peer_store reference, aligning with the new &PersistentPeerStorage signature.

Also applies to: 1015-1017

dash-spv/src/network/reputation.rs (6)

8-8: LGTM! Updated imports for serde and storage.

Correctly imports Deserializer for custom deserialization and the new storage types from the storage module.

Also applies to: 15-15


80-92: LGTM! New constants and helper for reputation bounds.

The new constants properly define:

  • MAX_MISBEHAVIOR_SCORE (100) - ban threshold
  • MIN_MISBEHAVIOR_SCORE (-50) - best possible reputation
  • MAX_BAN_COUNT and MAX_ACTION_COUNT - guards against corrupted data

The default_instant() helper provides a consistent default for Instant fields.


94-134: LGTM! Defensive deserialization with clamping.

The custom deserializers provide robust handling of potentially corrupted or malicious persistence data:

  • clamp_peer_score: Clamps to valid score range with logging
  • clamp_peer_ban_count: Caps excessive ban counts
  • clamp_peer_connection_attempts: Limits connection attempts

This is good defensive programming for data loaded from disk.


137-171: LGTM! PeerReputation serde configuration.

The struct correctly:

  • Derives Serialize and Deserialize
  • Uses custom deserializers for clamped fields
  • Skips Instant-based fields that can't be serialized
  • Provides defaults for skipped fields via default_instant()

219-219: LGTM! Score clamping uses consistent bounds.

Both apply_decay and update_reputation now use the named constants MIN_MISBEHAVIOR_SCORE and MAX_MISBEHAVIOR_SCORE for consistent bounds enforcement.

Also applies to: 283-283


454-458: LGTM! Storage-backed persistence implementation.

The persistence methods correctly:

  • Accept &PersistentPeerStorage instead of file paths
  • Delegate to the storage trait methods
  • Include validation during load (connection count consistency, action count sanity checks)

One note on lines 483-485: Previously banned peers start with score.max(50), which means they're closer to the ban threshold. This is intentional defensive behavior to be more cautious with historically problematic peers.

Also applies to: 461-497

dash-spv/src/storage/peers.rs (2)

22-53: LGTM! Clean trait and struct design.

The trait design with immutable &self references aligns well with concurrent access patterns, and the helper methods provide clear path construction logic.


55-69: LGTM! Appropriate PersistentStorage implementation.

The no-op persist() is correctly documented as data is written immediately during save operations.

@ZocoLini ZocoLini force-pushed the refactor/peer-persistence branch from 732c3ac to 68750bc Compare January 8, 2026 22:22
@github-actions github-actions bot added the merge-conflict The PR conflicts with the target branch. label Jan 8, 2026
@github-actions
Copy link

github-actions bot commented Jan 8, 2026

This PR has merge conflicts with the base branch. Please rebase or merge the base branch into your branch to resolve them.

@github-actions github-actions bot removed the merge-conflict The PR conflicts with the target branch. label Jan 8, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
dash-spv/src/storage/peers.rs (2)

149-160: Consider using spawn_blocking for consistency with load_peers.

While reputation JSON files are typically small, using spawn_blocking would maintain consistency with the load_peers implementation pattern and prevent potential executor blocking for larger reputation datasets.

♻️ Proposed refactor for consistency
 async fn load_peers_reputation(&self) -> StorageResult<HashMap<SocketAddr, PeerReputation>> {
     let reputation_file = self.peers_reputation_file();

     if !fs::try_exists(&reputation_file).await? {
         return Ok(HashMap::new());
     }

-    let json = fs::read_to_string(reputation_file).await?;
-    serde_json::from_str(&json).map_err(|e| {
-        StorageError::ReadFailed(format!("Failed to deserialize peers reputations: {e}"))
-    })
+    tokio::task::spawn_blocking(move || {
+        let json = std::fs::read_to_string(reputation_file)?;
+        serde_json::from_str(&json).map_err(|e| {
+            StorageError::ReadFailed(format!("Failed to deserialize peers reputations: {e}"))
+        })
+    })
+    .await
+    .map_err(|e| StorageError::ReadFailed(format!("Failed to load peers reputation: {e}")))?
 }

163-207: Add test coverage for reputation persistence.

The tests cover basic peer save/load operations well, but there's no coverage for save_peers_reputation and load_peers_reputation. Consider adding tests for:

  • Saving and loading peer reputations
  • Loading from empty reputation storage
  • Multiple peers and reputations
📝 Example test for reputation persistence
#[tokio::test]
async fn test_persistent_peer_storage_reputation() {
    let temp_dir = TempDir::new().expect("Failed to create temporary directory for test");
    let store = PersistentPeerStorage::open(temp_dir.path())
        .await
        .expect("Failed to open persistent peer storage");

    let addr: SocketAddr = "192.168.1.1:9999".parse().unwrap();
    let mut reputations = HashMap::new();
    reputations.insert(addr, PeerReputation::default()); // Adjust based on PeerReputation API

    store.save_peers_reputation(&reputations)
        .await
        .expect("Failed to save reputations");

    let loaded = store.load_peers_reputation()
        .await
        .expect("Failed to load reputations");
    
    assert_eq!(loaded.len(), 1);
    assert!(loaded.contains_key(&addr));
}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a7925a and d45fe90.

📒 Files selected for processing (2)
  • dash-spv/src/storage/peers.rs
  • dash-spv/tests/header_sync_test.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • dash-spv/tests/header_sync_test.rs
🧰 Additional context used
📓 Path-based instructions (3)
dash-spv/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

dash-spv/**/*.rs: Use async/await throughout the codebase, built on tokio runtime
Use Arc for trait objects to enable runtime polymorphism for NetworkManager and StorageManager
Use Tokio channels for inter-component message passing between async tasks
Maintain minimum Rust version (MSRV) of 1.89 and use only compatible syntax and features

Files:

  • dash-spv/src/storage/peers.rs
dash-spv/src/storage/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

Store headers in 10,000-header segments with index files in headers/ directory, filter headers and compact block filters in filters/ directory, and state in state/ directory

Files:

  • dash-spv/src/storage/peers.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: MSRV (Minimum Supported Rust Version) is 1.89; ensure compatibility with this version for all builds
Unit tests should live alongside code with #[cfg(test)] annotation; integration tests use the tests/ directory
Use snake_case for function and variable names
Use UpperCamelCase for types and traits
Use SCREAMING_SNAKE_CASE for constants
Format code with rustfmt before commits; ensure cargo fmt --all is run
Run cargo clippy --workspace --all-targets -- -D warnings for linting; avoid warnings in CI
Prefer async/await via tokio for asynchronous operations

**/*.rs: Never hardcode network parameters, addresses, or keys in Rust code
Use proper error types (thiserror) and propagate errors appropriately in Rust
Use tokio runtime for async operations in Rust
Use conditional compilation with feature flags for optional features
Write unit tests for new functionality in Rust
Format code using cargo fmt
Run clippy with all features and all targets, treating warnings as errors
Never log or expose private keys in any code
Always validate inputs from untrusted sources in Rust
Use secure random number generation for keys

Files:

  • dash-spv/src/storage/peers.rs
🧠 Learnings (15)
📓 Common learnings
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/network/**/*.rs : Use DNS-first peer discovery with automatic DNS seeds (dnsseed.dash.org, testnet-seed.dashdot.io) when no explicit peers are configured; implement immediate startup with 10-second delay only for subsequent peer searches
📚 Learning: 2025-06-26T15:54:02.509Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:54:02.509Z
Learning: The `StorageManager` trait in `dash-spv/src/storage/mod.rs` uses `&mut self` methods but is also `Send + Sync`, and implementations often use interior mutability for concurrency. This can be confusing, so explicit documentation should clarify thread-safety expectations and the rationale for the API design.

Applied to files:

  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/network/**/*.rs : Use DNS-first peer discovery with automatic DNS seeds (dnsseed.dash.org, testnet-seed.dashdot.io) when no explicit peers are configured; implement immediate startup with 10-second delay only for subsequent peer searches

Applied to files:

  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/tests/**/*.rs : Organize tests into unit tests (in-module), integration tests (tests/ directory), real network tests (with live Dash Core nodes), and performance benchmarks

Applied to files:

  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/storage/**/*.rs : Store headers in 10,000-header segments with index files in headers/ directory, filter headers and compact block filters in filters/ directory, and state in state/ directory

Applied to files:

  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/src/**/*.rs : Use thread-local storage for error propagation via `dash_spv_ffi_get_last_error()` function

Applied to files:

  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/sync/**/*.rs : Implement sequential phase-based synchronization via SyncManager with phases progressing in order: Headers → Masternode List → Filter Headers → Filters → Blocks

Applied to files:

  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/**/*.rs : Use Arc<dyn TraitName> for trait objects to enable runtime polymorphism for NetworkManager and StorageManager

Applied to files:

  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/**/*.rs : Use async/await throughout the codebase, built on tokio runtime

Applied to files:

  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/tests/unit/**/*.rs : Add corresponding unit tests in `tests/unit/` for each new FFI function

Applied to files:

  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-06-26T16:02:42.390Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:02:42.390Z
Learning: Passing exclusive mutable references to network and storage managers into the sync manager in async Rust can lead to borrow checker issues or runtime contention if concurrent access is needed elsewhere. It's advisable to document this architectural tradeoff and consider refactoring to interior mutability or message passing for shared access as the codebase evolves.

Applied to files:

  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-22T17:59:51.097Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-22T17:59:51.097Z
Learning: Applies to **/*.rs : Prefer `async`/`await` via `tokio` for asynchronous operations

Applied to files:

  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/**/*.rs : Use Tokio channels for inter-component message passing between async tasks

Applied to files:

  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/network/**/*.rs : Implement configurable timeouts with recovery mechanisms for network operations

Applied to files:

  • dash-spv/src/storage/peers.rs
📚 Learning: 2025-12-30T22:00:57.000Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-30T22:00:57.000Z
Learning: Applies to **/*.rs : Use tokio runtime for async operations in Rust

Applied to files:

  • dash-spv/src/storage/peers.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
  • GitHub Check: Windows / tools
  • GitHub Check: Windows / ffi
  • GitHub Check: macOS / ffi
  • GitHub Check: macOS / spv
  • GitHub Check: macOS / tools
  • GitHub Check: fuzz (hashes_sha512_256)
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (hashes_sha512)
  • GitHub Check: fuzz (dash_deserialize_script)
  • GitHub Check: fuzz (hashes_ripemd160)
  • GitHub Check: fuzz (hashes_sha1)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (dash_deserialize_witness)
  • GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
  • GitHub Check: fuzz (dash_deser_net_msg)
  • GitHub Check: Address Sanitizer
  • GitHub Check: Pre-commit (windows-latest)
  • GitHub Check: Thread Sanitizer
  • GitHub Check: conflict-check
🔇 Additional comments (7)
dash-spv/src/storage/peers.rs (7)

1-16: LGTM: Clean import organization.

The imports are well-organized and all necessary for the functionality. The mix of sync (std::fs::File) and async (tokio::fs) types is appropriate for the spawn_blocking pattern used later.


18-33: LGTM: Well-designed trait interface.

The PeerStorage trait provides a clean separation between peer address storage and reputation tracking, with appropriate async signatures consistent with the storage layer patterns.


35-49: LGTM: Clean helper methods for path management.

The helper methods provide clear, centralized path construction for peer data and reputation files.


51-65: LGTM: Appropriate implementation of PersistentStorage.

The no-op persist() method is well-documented and makes sense since data is atomically written during save operations.


69-91: LGTM: Robust peer persistence with atomic writes.

The implementation correctly uses consensus encoding and atomic writes to safely persist peer data. The defensive parent directory check is good practice even though the path structure guarantees its existence.


93-128: Excellent use of spawn_blocking for sync I/O operations.

The implementation correctly offloads synchronous file I/O operations to a blocking thread pool using spawn_blocking, preventing the tokio executor from being blocked. The EOF handling is proper, and the silent filtering of invalid socket addresses (line 125) provides resilience against corrupted entries.


130-147: LGTM: Consistent reputation persistence pattern.

The implementation follows the same safe persistence pattern as save_peers, with appropriate JSON serialization and atomic writes. Pretty-printing provides debugging benefits with minimal overhead.

@ZocoLini ZocoLini marked this pull request as draft January 9, 2026 00:02
@ZocoLini ZocoLini force-pushed the refactor/peer-persistence branch from d45fe90 to 5626344 Compare January 9, 2026 15:13
@ZocoLini ZocoLini marked this pull request as ready for review January 9, 2026 15:15
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
dash-spv/src/network/manager.rs (1)

42-42: Consider using trait object for storage polymorphism.

Per the coding guidelines, Arc<dyn TraitName> should be used for trait objects to enable runtime polymorphism. Using Arc<dyn PeerStorage> instead of Arc<PersistentPeerStorage> would allow swapping storage implementations (e.g., for testing with in-memory storage).

This isn't blocking since the current concrete type works correctly, but could improve testability and flexibility.

dash-spv/src/network/reputation.rs (1)

156-166: Consider adding clamp deserializers for positive_actions, negative_actions, and successful_connections.

The connection_attempts field has a clamp deserializer (clamp_peer_connection_attempts), but positive_actions, negative_actions, and successful_connections don't. The validation in load_from_storage (lines 474-480) checks these against MAX_ACTION_COUNT but rejects the entry entirely rather than clamping.

For consistency, consider either:

  1. Adding clamp deserializers for these fields (like connection_attempts)
  2. Or documenting why these fields should reject rather than clamp

This is a minor inconsistency that doesn't affect correctness.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3f65712 and b5acafb.

📒 Files selected for processing (3)
  • dash-spv/src/network/manager.rs
  • dash-spv/src/network/reputation.rs
  • dash-spv/src/network/reputation_tests.rs
🧰 Additional context used
📓 Path-based instructions (4)
dash-spv/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

dash-spv/**/*.rs: Use async/await throughout the codebase, built on tokio runtime
Use Arc for trait objects to enable runtime polymorphism for NetworkManager and StorageManager
Use Tokio channels for inter-component message passing between async tasks
Maintain minimum Rust version (MSRV) of 1.89 and use only compatible syntax and features

Files:

  • dash-spv/src/network/manager.rs
  • dash-spv/src/network/reputation_tests.rs
  • dash-spv/src/network/reputation.rs
dash-spv/src/network/**/*.rs

📄 CodeRabbit inference engine (dash-spv/CLAUDE.md)

dash-spv/src/network/**/*.rs: Use DNS-first peer discovery with automatic DNS seeds (dnsseed.dash.org, testnet-seed.dashdot.io) when no explicit peers are configured; implement immediate startup with 10-second delay only for subsequent peer searches
Implement configurable timeouts with recovery mechanisms for network operations

Files:

  • dash-spv/src/network/manager.rs
  • dash-spv/src/network/reputation_tests.rs
  • dash-spv/src/network/reputation.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: MSRV (Minimum Supported Rust Version) is 1.89; ensure compatibility with this version for all builds
Unit tests should live alongside code with #[cfg(test)] annotation; integration tests use the tests/ directory
Use snake_case for function and variable names
Use UpperCamelCase for types and traits
Use SCREAMING_SNAKE_CASE for constants
Format code with rustfmt before commits; ensure cargo fmt --all is run
Run cargo clippy --workspace --all-targets -- -D warnings for linting; avoid warnings in CI
Prefer async/await via tokio for asynchronous operations

**/*.rs: Never hardcode network parameters, addresses, or keys in Rust code
Use proper error types (thiserror) and propagate errors appropriately in Rust
Use tokio runtime for async operations in Rust
Use conditional compilation with feature flags for optional features
Write unit tests for new functionality in Rust
Format code using cargo fmt
Run clippy with all features and all targets, treating warnings as errors
Never log or expose private keys in any code
Always validate inputs from untrusted sources in Rust
Use secure random number generation for keys

Files:

  • dash-spv/src/network/manager.rs
  • dash-spv/src/network/reputation_tests.rs
  • dash-spv/src/network/reputation.rs
**/*test*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*test*.rs: Test both mainnet and testnet configurations
Use proptest for property-based testing where appropriate

Files:

  • dash-spv/src/network/reputation_tests.rs
🧠 Learnings (20)
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/network/**/*.rs : Use DNS-first peer discovery with automatic DNS seeds (dnsseed.dash.org, testnet-seed.dashdot.io) when no explicit peers are configured; implement immediate startup with 10-second delay only for subsequent peer searches

Applied to files:

  • dash-spv/src/network/manager.rs
  • dash-spv/src/network/reputation_tests.rs
  • dash-spv/src/network/reputation.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/**/*.rs : Use Arc<dyn TraitName> for trait objects to enable runtime polymorphism for NetworkManager and StorageManager

Applied to files:

  • dash-spv/src/network/manager.rs
  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-06-26T15:54:02.509Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T15:54:02.509Z
Learning: The `StorageManager` trait in `dash-spv/src/storage/mod.rs` uses `&mut self` methods but is also `Send + Sync`, and implementations often use interior mutability for concurrency. This can be confusing, so explicit documentation should clarify thread-safety expectations and the rationale for the API design.

Applied to files:

  • dash-spv/src/network/manager.rs
  • dash-spv/src/network/reputation_tests.rs
  • dash-spv/src/network/reputation.rs
📚 Learning: 2025-06-26T16:02:42.390Z
Learnt from: DCG-Claude
Repo: dashpay/rust-dashcore PR: 0
File: :0-0
Timestamp: 2025-06-26T16:02:42.390Z
Learning: Passing exclusive mutable references to network and storage managers into the sync manager in async Rust can lead to borrow checker issues or runtime contention if concurrent access is needed elsewhere. It's advisable to document this architectural tradeoff and consider refactoring to interior mutability or message passing for shared access as the codebase evolves.

Applied to files:

  • dash-spv/src/network/manager.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/sync/**/*.rs : Implement sequential phase-based synchronization via SyncManager with phases progressing in order: Headers → Masternode List → Filter Headers → Filters → Blocks

Applied to files:

  • dash-spv/src/network/manager.rs
  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/storage/**/*.rs : Store headers in 10,000-header segments with index files in headers/ directory, filter headers and compact block filters in filters/ directory, and state in state/ directory

Applied to files:

  • dash-spv/src/network/manager.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/src/error.rs : Use domain-specific error types (NetworkError, StorageError, SyncError, ValidationError, SpvError) rather than generic error handling

Applied to files:

  • dash-spv/src/network/manager.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/**/*.rs : Use async/await throughout the codebase, built on tokio runtime

Applied to files:

  • dash-spv/src/network/manager.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/tests/**/*.rs : Organize tests into unit tests (in-module), integration tests (tests/ directory), real network tests (with live Dash Core nodes), and performance benchmarks

Applied to files:

  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-22T17:59:51.097Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-22T17:59:51.097Z
Learning: Cover critical parsing, networking, SPV, and wallet flows in tests; add regression tests for fixes; consider property tests with `proptest`

Applied to files:

  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-16T09:03:55.811Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv/CLAUDE.md:0-0
Timestamp: 2025-12-16T09:03:55.811Z
Learning: Applies to dash-spv/tests/**/*.rs : Integration tests should gracefully handle unavailable Dash Core nodes at 127.0.0.1:9999 without failing the test suite

Applied to files:

  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-01T07:59:58.608Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: dash-spv-ffi/CLAUDE.md:0-0
Timestamp: 2025-12-01T07:59:58.608Z
Learning: Applies to dash-spv-ffi/tests/unit/**/*.rs : Add corresponding unit tests in `tests/unit/` for each new FFI function

Applied to files:

  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/tests/**/*.rs : Organize unit tests by functionality: separate test files for BIP32, mnemonics, addresses, derivation paths, and PSBT operations

Applied to files:

  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-19T00:07:22.904Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: key-wallet/CLAUDE.md:0-0
Timestamp: 2025-12-19T00:07:22.904Z
Learning: Applies to key-wallet/**/tests/**/*.rs : Use deterministic testing with known test vectors and fixed seeds for reproducible results

Applied to files:

  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-30T22:00:57.000Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-30T22:00:57.000Z
Learning: Applies to **/*integration*.rs : Write integration tests for network operations

Applied to files:

  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-22T17:59:51.097Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-22T17:59:51.097Z
Learning: Applies to **/*.rs : Unit tests should live alongside code with `#[cfg(test)]` annotation; integration tests use the `tests/` directory

Applied to files:

  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-22T17:59:51.097Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-22T17:59:51.097Z
Learning: Applies to **/tests/**/*.rs : Use descriptive test names (e.g., `test_parse_address_mainnet`)

Applied to files:

  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-30T22:00:57.000Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-30T22:00:57.000Z
Learning: Applies to **/*test*.rs : Test both mainnet and testnet configurations

Applied to files:

  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-22T17:59:51.097Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-22T17:59:51.097Z
Learning: Applies to **/tests/**/*.rs : Mark network-dependent or long-running tests with `#[ignore]` and run with `-- --ignored`

Applied to files:

  • dash-spv/src/network/reputation_tests.rs
📚 Learning: 2025-12-30T22:00:57.000Z
Learnt from: CR
Repo: dashpay/rust-dashcore PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-30T22:00:57.000Z
Learning: Applies to **/*.rs : Write unit tests for new functionality in Rust

Applied to files:

  • dash-spv/src/network/reputation_tests.rs
🧬 Code graph analysis (2)
dash-spv/src/network/manager.rs (2)
dash-spv/src/storage/mod.rs (1)
  • open (52-52)
dash-spv/src/storage/peers.rs (1)
  • open (53-59)
dash-spv/src/network/reputation_tests.rs (2)
dash-spv/src/storage/mod.rs (1)
  • open (52-52)
dash-spv/src/storage/peers.rs (1)
  • open (53-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Ubuntu ARM / rpc
  • GitHub Check: macOS / spv
  • GitHub Check: fuzz (hashes_json)
  • GitHub Check: fuzz (hashes_cbor)
  • GitHub Check: fuzz (hashes_ripemd160)
  • GitHub Check: fuzz (hashes_sha1)
  • GitHub Check: fuzz (dash_outpoint_string)
  • GitHub Check: fuzz (dash_deserialize_block)
  • GitHub Check: fuzz (dash_deserialize_witness)
  • GitHub Check: fuzz (dash_deserialize_script)
  • GitHub Check: fuzz (hashes_sha256)
  • GitHub Check: fuzz (dash_deser_net_msg)
  • GitHub Check: fuzz (dash_deserialize_address)
  • GitHub Check: fuzz (dash_script_bytes_to_asm_fmt)
  • GitHub Check: Pre-commit (ubuntu-latest)
  • GitHub Check: Pre-commit (windows-latest)
  • GitHub Check: Pre-commit (macos-latest)
  • GitHub Check: Thread Sanitizer
  • GitHub Check: Address Sanitizer
  • GitHub Check: conflict-check
🔇 Additional comments (13)
dash-spv/src/network/reputation_tests.rs (2)

5-6: LGTM!

The imports are correctly updated to use the new storage abstractions (PersistentPeerStorage and PersistentStorage) consistent with the storage refactor across the codebase.


66-73: LGTM!

The persistence test correctly migrates to the new storage abstraction pattern. Using tempfile::TempDir ensures proper test isolation, and the PersistentPeerStorage::open() followed by save_to_storage/load_from_storage correctly exercises the new storage API.

dash-spv/src/network/manager.rs (4)

30-30: LGTM!

Import correctly updated to use the new storage module types (PeerStorage, PersistentPeerStorage, PersistentStorage), aligning with the trait-based storage abstraction introduced in this PR.


84-90: LGTM!

The initialization correctly:

  1. Opens storage asynchronously with proper error propagation via ?
  2. Creates the reputation manager
  3. Attempts to load reputation data with graceful degradation (warning on failure)

This pattern ensures the node can start even if reputation data is missing or corrupted.


741-743: LGTM!

The &*peer_store pattern correctly dereferences the Arc to obtain a reference to the underlying PersistentPeerStorage for the trait method call. Error handling appropriately logs warnings without failing the maintenance loop.


1015-1017: LGTM!

Shutdown correctly saves reputation data to ensure persistence across restarts. The error handling appropriately logs warnings without blocking the shutdown process.

dash-spv/src/network/reputation.rs (7)

8-8: LGTM!

The import updates correctly bring in the required serde traits (Deserialize, Deserializer, Serialize) and the PeerStorage trait for the storage abstraction.

Also applies to: 15-15


80-92: LGTM!

The new constants establish sensible bounds:

  • MAX_MISBEHAVIOR_SCORE (100) aligns with the ban threshold
  • MIN_MISBEHAVIOR_SCORE (-50) allows meaningful positive reputation
  • MAX_BAN_COUNT and MAX_ACTION_COUNT prevent potential overflow from corrupted data

The default_instant() function provides a reasonable default for #[serde(skip)] fields.


94-134: LGTM!

The custom deserializers implement defensive validation that protects against corrupted or malicious data. The warning logs help identify when clamping occurs, aiding debugging. This approach moves validation into serde's deserialization pipeline, which is cleaner than post-deserialization validation.


219-219: LGTM!

The decay logic correctly clamps the score to MIN_MISBEHAVIOR_SCORE (-50), preventing reputation from decaying below the best possible score. This aligns with the new bounds system.


282-283: LGTM!

The score update correctly uses .clamp(MIN_MISBEHAVIOR_SCORE, MAX_MISBEHAVIOR_SCORE) to ensure the score stays within valid bounds after any reputation change.


454-458: LGTM!

The save_to_storage method correctly:

  1. Uses generic &impl PeerStorage for flexibility
  2. Delegates to the storage abstraction
  3. Maps storage errors to std::io::Error for API compatibility

461-497: LGTM!

The load_from_storage method implements robust validation:

  1. Clamps successful_connections to not exceed connection_attempts
  2. Skips entries with corrupted action counts (defensive rejection)
  3. Previously banned peers start with score ≥50 (halfway to ban threshold), appropriately distrusting them

The logging provides good visibility into loaded/skipped entries.

Copy link
Collaborator

@xdustinface xdustinface left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about just creating a struct like below to merge it both into one so we don't have two separate files / APIs and only one manager for all the peer stuff in the network manager?

struct StoredPeer {
    address: AddrV2Message,
    reputation: PeerReputation,
}

If not for this PR, maybe as follow up?


const MAX_ACTION_COUNT: u64 = 1_000_000;

fn default_instant() -> Instant {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need an extra helper for this?

pub banned_until: Option<Instant>,

/// Last time the reputation was updated
#[serde(skip, default = "default_instant")]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[serde(skip, default = "default_instant")]
#[serde(skip, default = "Instant::now")]

Why not just like this ^ ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants